using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;

namespace Charisma
{
    public partial class DbContext : IDisposable
    {
        private IDbProvider dbProvider;

        protected DbContext()
            : this(null, true)
        {
        }

        public DbContext(string connectionStringOrName)
            : this(connectionStringOrName, false)
        {
        }

        private DbContext(string connectionStringOrName, bool useTypeConvention)
        {
            if (useTypeConvention)
                connectionStringOrName = GetType().Name;
            ConnectionSettings = ConfigurationManager.ConnectionStrings[connectionStringOrName];
            // TODO: implement convention for connection string
//            if (ConnectionSettings == null)
//                ConnectionSettings = new ConnectionStringSettings();
            Initialize();
        }

        public ConnectionStringSettings ConnectionSettings { get; private set; }

        public IDatabase Database { get; private set; }

        protected DbModel DbModel { get; private set; }

        protected virtual void OnModelCreating(DbModelBuilder modelBuilder)
        {
            
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (Database == null)
                return;
            Database.Dispose();
            Database = null;
        }

        #endregion

        private IDbProvider GetDbProvider()
        {
            string providerName = ConnectionSettings.ProviderName;
            string extensionAssemblyName = providerName;
            AssemblyName mainAssemblyRef = Assembly.GetCallingAssembly().GetName();
            string extensionAssemblyFullName = mainAssemblyRef.FullName.Replace(mainAssemblyRef.Name,
                                                                                extensionAssemblyName);
            Assembly assembly = Assembly.Load(extensionAssemblyFullName);
            Type dbProvider = assembly.GetExportedTypes()
                .SingleOrDefault(t => t.Is<IDbProvider>());
            if (dbProvider == null)
                throw new InvalidOperationException(
                    string.Format("Unable to create db provider for connection string '{0}'", ConnectionSettings.Name));
            return (IDbProvider) Activator.CreateInstance(dbProvider);
        }

        [ContractInvariantMethod]
        private void ObjectInvariant()
        {
            Contract.Invariant(dbProvider != null);
            Contract.Invariant(ConnectionSettings != null);
        }
    }
}